# -*- coding: utf-8 -*-
"""Sample controller module"""

# turbogears imports
from tg import expose
from tg import redirect, validate, flash, require
from repoze.what import predicates

# third party imports
#from pylons.i18n import ugettext as _
#from repoze.what import predicates

# project specific imports
from crv.lib.base import BaseController
from tg.controllers import RestController
from crv.model import DBSession, metadata
from crv.model.crvmodel import *
from tgext.crud import CrudRestController

from sprox.tablebase import TableBase
from sprox.fillerbase import TableFiller, EditFormFiller
from sprox.formbase import EditableForm, AddRecordForm

#from sprox.dojo.tablebase import DojoTableBase 
#from sprox.dojo.fillerbase import DojoTableFiller
#from sprox.dojo.formbase import AddRecordForm

from crv.tools.certstoremgr import CertStoreMgr
from crv.tools.vmcertnamehelper import CertName2VMID

from tg import request
import cgi

import logging
log = logging.getLogger(__name__)

import tg

tmpstr = tg.config.get('cloudcrv.usessl', 'False')

CloudCRV_UseSSL = False

if tmpstr=="True":
    CloudCRV_UseSSL = True

#for certificate verification
from M2Crypto import X509, SSL
    
class VMController(CrudRestController):
    model = VM

    class new_form_type(AddRecordForm):
        __model__ = VM
        __omit_fields__ = ['VM_ID']

    class edit_form_type(EditableForm):
        __model__ = VM
        __omit_fields__ = ['VM_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = VM
        
    class table_type(TableBase):
        __model__ = VM
        __xml_fields__ = ['role']
        
    class table_filler_type(TableFiller):
        __model__ = VM
        def role(self, obj):
            roles = ', '.join(['<a href="/roles/'+str(d.Role_ID)+'/edit">'+d.getAttr("name")+'</a>'
                               for d in obj.role])
            return roles.join(('<div>', '</div>'))

        def attr(self,obj):
            attrs= ", ".join([iattr.name+'='+iattr.value for iattr in obj.attr])
            return attrs
        
        def currentSC(self,obj):
            return obj.SCname[obj.currentSC]

        def targetSC(self,obj):
            return obj.SCname[obj.targetSC]
        
class ClusterController(CrudRestController):
    model = Cluster

    class new_form_type(AddRecordForm):
        __model__ = Cluster
        __omit_fields__ = ['Cluster_ID']

    class edit_form_type(EditableForm):
        __model__ = Cluster
        __omit_fields__ = ['Cluster_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = Cluster
        
    class table_type(TableBase):
        __model__ = Cluster
        __xml_fields__ = ['role']
        
    class table_filler_type(TableFiller):
        __model__ = Cluster
        def role(self, obj):
            roles = ', '.join(['<a href="/roles/'+str(d.Role_ID)+'/edit">'+d.getAttr("name")+'</a>'
                               for d in obj.role])
            return roles.join(('<div>', '</div>'))
        
        def attr(self,obj):
            attrs= ", ".join([iattr.name+'='+iattr.value for iattr in obj.attr])
            return attrs

        def currentSC(self,obj):
            return obj.SCname[obj.currentSC]

        def targetSC(self,obj):
            return obj.SCname[obj.targetSC]
        
class RoleController(CrudRestController):
    model = Role

    class new_form_type(AddRecordForm):
        __model__ = Role
        __omit_fields__ = ['Role_ID']

    class edit_form_type(EditableForm):
        __model__ = Role
        __omit_fields__ = ['Role_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = Role
        
    class table_type(TableBase):
        __model__ = Role
        __xml_fields__ = ['vm','cluster','roleDef','dependOn']
        
    class table_filler_type(TableFiller):
        __model__ = Role
        def vm(self, obj):
            vms = '<a href="/vms/'+str(obj.vm.VM_ID)+'/edit">'+obj.vm.getAttr("name")+'</a>'
            return vms.join(('<div>', '</div>'))
        
        def roleDef(self, obj):
            roledefs = '<a href="/roledefs/'+str(obj.roleDef.RoleDef_ID)+'/edit">'+obj.roleDef.getAttr("name")+'</a>'
            return roledefs.join(('<div>', '</div>'))
        
        def cluster(self, obj):
            clusters = ', '.join(['<a href="/clusters/'+str(d.Cluster_ID)+'/edit">'+d.getAttr("name")+'</a>'
                               for d in obj.cluster])
            return clusters.join(('<div>', '</div>'))
        
        def dependOn(self, obj):
            dependons = ', '.join(['<a href="/roles/'+str(d.Role_ID)+'/edit">'+d.getAttr("name")+'</a>'
                               for d in obj.dependOn])
            return dependons.join(('<div>', '</div>'))
        
        def currentSC(self,obj):
            return obj.SCname[obj.currentSC]

        def targetSC(self,obj):
            return obj.SCname[obj.targetSC]

        def attr(self,obj):
            attrs= ", ".join([iattr.name+'='+iattr.value for iattr in obj.attr])
            return attrs

        
class RoleDefController(CrudRestController):
    model = RoleDef

    class new_form_type(AddRecordForm):
        __model__ = RoleDef
        __omit_fields__ = ['RoleDef_ID']

    class edit_form_type(EditableForm):
        __model__ = RoleDef
        __omit_fields__ = ['RoleDef_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = RoleDef
        
    class table_type(TableBase):
        __model__ = RoleDef
        __xml_fields__=['dependOn','dependOnLocal','dependBy','dependByLocal']
        __omit_fields__ = ['dummyVal']
        
    class table_filler_type(TableFiller):
        __model__ = RoleDef
        def dependOn(self, obj):
            dependons = ', '.join(['<a href="/roledefs/'+str(d.RoleDef_ID)+'/edit">'+d.getAttr("name")+'</a>' for d in obj.dependOn])
            return dependons.join(('<div>', '</div>'))

        def dependOnLocal(self, obj):
            roles = ', '.join(['<a href="/roledefs/'+str(d.RoleDef_ID)+'/edit">'+d.getAttr("name")+'</a>' for d in obj.dependOnLocal])
            return roles.join(('<div>', '</div>'))

        def dependBy(self, obj):
            dependons = ', '.join(['<a href="/roledefs/'+str(d.RoleDef_ID)+'/edit">'+d.getAttr("name")+'</a>' for d in obj.dependBy])
            return dependons.join(('<div>', '</div>'))

        def dependByLocal(self, obj):
            roles = ', '.join(['<a href="/roledefs/'+str(d.RoleDef_ID)+'/edit">'+d.getAttr("name")+'</a>' for d in obj.dependByLocal])
            return roles.join(('<div>', '</div>'))

        def attr(self,obj):
            attrs= ", ".join([iattr.name+'='+iattr.value for iattr in obj.attr])
            return attrs
        
        def currentSC(self,obj):
            return obj.SCname[obj.currentSC]

        def targetSC(self,obj):
            return obj.SCname[obj.targetSC]
        
        

class ClusterAttrController(CrudRestController):
    model = ClusterAttr

    class new_form_type(AddRecordForm):
        __model__ = ClusterAttr
        __omit_fields__ = ['ClusterAttr_ID']

    class edit_form_type(EditableForm):
        __model__ = ClusterAttr
        __omit_fields__ = ['ClusterAttr_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = ClusterAttr
        
    class table_type(TableBase):
        __model__ = ClusterAttr
        
    class table_filler_type(TableFiller):
        __model__ = ClusterAttr
        

class RoleAttrController(CrudRestController):
    model = RoleAttr

    class new_form_type(AddRecordForm):
        __model__ = RoleAttr
        __omit_fields__ = ['RoleAttr_ID']

    class edit_form_type(EditableForm):
        __model__ = RoleAttr
        __omit_fields__ = ['RoleAttr_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = RoleAttr
        
    class table_type(TableBase):
        __model__ = RoleAttr
        
    class table_filler_type(TableFiller):
        __model__ = RoleAttr
        

class VMAttrController(CrudRestController):
    model = VMAttr

    class new_form_type(AddRecordForm):
        __model__ = VMAttr
        __omit_fields__ = ['VMAttr_ID']

    class edit_form_type(EditableForm):
        __model__ = VMAttr
        __omit_fields__ = ['VMAttr_ID']
        
    class edit_filler_type(EditFormFiller):
        __model__ = VMAttr
        
    class table_type(TableBase):
        __model__ = VMAttr
        
    class table_filler_type(TableFiller):
        __model__ = VMAttr
        
class StatusController(BaseController):

    @expose()
    def setVMAttr(self,vmid,name,value):

        (succ,errormsg)=self.verifyClient(vmid)
        if not succ:
            return errormsg

        vms = DBSession.query(VM).filter(VM.VM_ID==vmid).all()
        log.info("StatusController::setVMAttr::vmid=%s name=%s, attr=%s"%(vmid,name,value))
        if len(vms)<1:
            return "Error: No VM ID=%s found"%vmid
        
        vms[0].setAttr(name,value)
        transaction.commit()

        return "True"
        
    @expose()
    def getVMAttr(self,vmid,name):

        (succ,errormsg)=self.verifyClient(vmid)
        if not succ:
            log.error(errormsg)
            return "None"

        vms = DBSession.query(VM).filter(VM.VM_ID==vmid).all()
        log.info("StatusController::getVMAttr::vmid=%s name=%s"%(vmid,name))
        if len(vms)<1:
            log.error("Error: No VM ID=%s found"%vmid)
            return "None"
        ret = vms[0].getAttr(name)
        return str(ret)

    @expose()
    def setRoleAttr(self,roleid,name,value):

        roles = DBSession.query(Role).filter(Role.Role_ID==roleid).all()
        log.info("StatusController::setRoleAttr::roleid=%s name=%s, attr=%s"%(roleid,name,value))
        if len(roles)<1:
            return "Error: No Role ID=%s found"%roleid

        (succ, errormsg)=self.verifyClient(roles[0].vm.VM_ID)
        if not succ:
            return errormsg

        roles[0].setAttr(name,value)
        transaction.commit()
        return "True"
        
    @expose()
    def getRoleAttr(self,roleid,name):

        roles = DBSession.query(Role).filter(Role.Role_ID==roleid).all()
        log.info("StatusController::getRoleAttr::roleid=%s name=%s"%(roleid,name))
        if len(roles)<1:
            log.error("Error: No Role ID=%s found"%roleid)
            return "None"

        (succ, errormsg)=self.verifyClient(roles[0].vm.VM_ID)
        if not succ:
            log.error(errormsg)
            return "None"
        
        ret=roles[0].getAttr(name)
        return str(ret)
            
    @expose()
    def getDependentAttr(self,roleid,name):

        roles = DBSession.query(Role).filter(Role.Role_ID==roleid).all()
        log.info("StatusController::getDependentAttr::roleid=%s name=%s"%(roleid,name))
        if len(roles)<1:
            transaction.commit()
            return "None"
        result=[]
        for irole in roles[0].dependBy:
            attr=irole.getAttr(name)
            if attr!=None:
                result.append(attr)
                
        if len(result)==0:
            return "None"
        return " ".join([a for a in result])
        
    @expose()
    def getProviderAttr(self,roleid,name):

        roles = DBSession.query(Role).filter(Role.Role_ID==roleid).all()
        log.info("StatusController::getProviderAttr::roleid=%s name=%s"%(roleid,name))
        log.info("StatusController::getProviderAttr::roleid=%s name=%s"%(roleid,name))
        if len(roles)<1:
            log.error("no role id=%s"%roleid)
            return "None"

        result=[]
        for irole in roles[0].dependOn:
            log.info("irole.name=%s, irole.attr=%s"%(irole.getAttr("name"), irole.getAttr(name)))
            attr=irole.getAttr(name)
            if attr!=None:
                result.append(attr)

        if len(result)==0:
            return "None"
        return " ".join(str(a) for a in result)
    
    @expose()
    def setClusterAttr(self,clusterid,name,value):


        clusters = DBSession.query(Cluster).filter(Cluster.Cluster_ID==clusterid).all()
        log.info("StatusController::setClusterAttr::clusterid=%s name=%s, attr=%s"%(clusterid,name,value))
        if len(clusters)<1:
            transaction.commit()
            return "Error: No Cluster ID=%s found"%clusterid

        vmid=self.getVMIDFromCert()
        if vmid is None:
            return "Error: can't get VMID from your certificate"

        exist=False
        for ivm in clusters.vm:
            if ivm.VM_ID==vmid:
                exist=True
                break
            
        if not exist:
            return "Error: VMID given by your cert ID=%s is not part of the Cluster ID=%s"%(vmid, cluster)

        (succ, errormsg)=self.verifyClient(vmid)
        if not succ:
            return errormsg

        clusters[0].setAttr(name,value)
        transaction.commit()
        return "True"
        
    @expose()
    def setVMActiveRoleSuccess(self,vmid):

        (succ,errormsg)=self.verifyClient(vmid)
        if not succ:
            return errormsg

        vms = DBSession.query(VM).filter(VM.VM_ID==vmid).all()
        log.info("StatusController::setVMActiveRoleSuccess::vmid=%s"%(vmid))
        if len(vms)<1:
            return "Error: VM ID=%s does not exist"%vmid
        
        vm=vms[0]

        for irole in vm.role:
            #In case of applying
            if irole.getAttr("ProfileProvided")=="True":
                irole.setAttr("ProfileApplied","True")
        
            #In case of removing
            if irole.getAttr("ReverseProfileProvided")=="True":
                irole.setAttr("ReverseProfileApplied","True")

        transaction.commit()
        return "True"

    @expose()
    def getRunUpdate(self,vmid,name):
        (succ,errormsg)=self.verifyClient(vmid)
        if not succ:
            return errormsg

        vms = DBSession.query(VM).filter(VM.VM_ID==vmid).all()
        log.info("StatusController::getClusterAttrByVM::vmid=%s name=%s"%(vmid,name))
        if len(vms)<1:
            return "Error: VM ID=%s does not exist"%vmid

        ret=vms[0].role[0].cluster.getAttr(name)
        return ret

    def getVMIDFromCert(self):
        """return (boolean, str(vmid))"""
        log.debug("SSL_CLIENT_CERT=%s\nSSL_CLIENT_S_DN_CN=%s\n"%(request.environ.get('SSL_CLIENT_CERT'), request.environ.get('SSL_CLIENT_S_DN_CN')))

        env_certcn=request.environ.get('SSL_CLIENT_S_DN_CN')

        if env_certcn is None or env_certcn is "":
            return None

        return str(CertName2VMID(env_certcn))

    def verifyClient(self, vmid):
        """will return tuple (boolean, errormessage)"""
        # boolean is whether the client passes the certificate verification

#        log.debug("SSL_CLIENT_CERT=%s\nSSL_CLIENT_S_DN_CN=%s\n"%(request.environ.get('SSL_CLIENT_CERT'), request.environ.get('SSL_CLIENT_S_DN_CN')))

        #authorized if localhost
        if request.environ.get('REMOTE_ADDR')=="127.0.0.1":
            return (True,"")

        env_cert = request.environ.get('SSL_CLIENT_CERT')
        env_certcn=request.environ.get('SSL_CLIENT_S_DN_CN')

        if env_cert is None or env_cert is "":
            return (False,"AuthFailed: SSL_CLIENT_CERT is empty, did you provided a cert?")
        if env_certcn is None or env_certcn is "":
            return (False,"AuthFailed: SSL_CLIENT_S_DN_CN is empty, did you provided a cert?")

        caMgr=CertStoreMgr.getInstance()
        if not caMgr.verify(env_cert):
            return (False, "AuthFailed: Your cert can't be verified")

        if vmid is None:
            #do not verify vmid if vmid is none
            return (True, "")

        if int(vmid)!=int(CertName2VMID(env_certcn)):
            return (False, "AuthFailed: Your cert say you are VM ID=%s, you are not authorized to modify properties (Roles, Clusters, VMs) for VM ID=%s"%(str(certvmid), str(vmid)))

        return (True, "")

    @require(predicates.not_anonymous("Only for logged in users"))
    @expose()
    def setClusterTargetSC(self, clusterid, targetsc):
        clusters = DBSession.query(Cluster).filter(Cluster.Cluster_ID==clusterid).all()
        log.info("StatusController::setClusterTargetSC::clusterid=%s, targetsc=%s"%(clusterid,str(targetsc)))
        if len(clusters)<1:
            transaction.commit()
            return "False"

        tsc=int(targetsc)

        #can set to SC_RUNNING only if STOPPED
        if tsc==clusters[0].SC.SC_RUNNING:
            if clusters[0].currentSC==clusters[0].targetSC and clusters[0].targetSC==clusters[0].SC.SC_INITIALIZED:
                clusters[0].targetSC=tsc
                transaction.commit()
                return "True"
            else:
                return "False"

        #can set to SC_STOPPED only if RUNNING
        if tsc==clusters[0].SC.SC_STOPPED:
            if clusters[0].currentSC==clusters[0].targetSC and clusters[0].targetSC==clusters[0].SC.SC_RUNNING:
                clusters[0].targetSC=tsc
                transaction.commit()
                return "True"
            else:
                return "False"

        
